﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Phoenix.Xna.Framework.Collections;
using Phoenix.Xna.Framework.ContentPipeline;
using Phoenix.Xna.Framework.Properties;
using Phoenix.Xna.Framework.Xml;
using Phoenix.Xna.Framework.Serialization;
using Phoenix.Xna.Framework.Reflection;

namespace Phoenix.Xna.Framework.Graphics
{
    public class EffectMap : IHasContent, IDisposableEx
    {
        [SerializableField("")]
        private string _effectAsset;
        [SerializableField("")]
        private string _currentTechnique = string.Empty;
        private bool _setParameters = true;

        public Effect Effect { get; set; }
        [SerializableProperty("")]
        public string ID { get; set; }
        [SerializableProperty("")]
        public string Tag { get; set; }
        [SerializableIList]
        public ValueBindingCollection Variables { get; set; }
        [SerializableIList]
        public TechniqueCollection Techniques { get; set; }
        [SerializableIList]
        public List<EffectMap> SubEffectMaps { get; set; }

        public EffectMap()
        {
            Variables = new ValueBindingCollection();
            Techniques = new TechniqueCollection();
            SubEffectMaps = new List<EffectMap>();
        }

        public void SetEffect(string asset)
        {
            _effectAsset = asset;
        }

        public void SetTechnique(string technique)
        {
            Techniques[technique].Activate();
        }

        public EffectPassCollection Begin()
        {
            if (!IsContentLoaded)
            {
                throw new InvalidOperationException(string.Format(Resources.EffectMapContentLoadedException, ID));
            }

            if (_setParameters)
            {
                Techniques[_currentTechnique].Load();
                _setParameters = false;
            }

            Effect.Begin();
            return Effect.CurrentTechnique.Passes;
        }

        public void End()
        {
            Effect.End();
        }

        public void SetVariable<T>(string variable, T value)
        {
            Variables[variable].SetValue<T>(value);            
        }

        public void SetParameter<T>(string parameter, T value)
        {
            SetParameter<T>(_currentTechnique, parameter, value);
        }

        public void SetParameter<T>(string technique, string parameter, T value)
        {
            Techniques[technique].SetParameter<T>(parameter, value);
            _setParameters = true;
        }

        public T GetVariable<T>(string variable)
        {
            return Variables[variable].GetValue<T>();
        }

        public T GetParameter<T>(string parameter)
        {
            return GetParameter<T>(_currentTechnique, parameter);
        }

        public T GetParameter<T>(string technique, string parameter)
        {
            return Techniques[technique].GetParameter<T>(parameter);
        }

        public void LoadParameter(string parameter)
        {
            LoadParameter(_currentTechnique, parameter);
        }

        public void LoadParameter(string technique, string parameter)
        {
            Techniques[technique].LoadParameter(parameter);
        }

        private void Dispose(bool disposing)
        {
            if (!IsDisposed && disposing)
            {
                UnloadContent();
            }

            IsDisposed = true;
        }

        #region IHasContent Members

        public bool IsContentLoaded { get; protected set; }

        public void LoadContent()
        {
            if (!IsContentLoaded)
            {
                Effect = Engine.Instance.Content.Load<Effect>(_effectAsset);

                if (!string.IsNullOrEmpty(_currentTechnique))
                {
                    Effect.CurrentTechnique = Effect.Techniques[_currentTechnique];
                }
                else
                {
                    Effect.CurrentTechnique = Effect.Techniques[0];
                }

                Techniques.Effect = Effect;
                Techniques.LoadContent();
                Variables.LoadContent();

                foreach (EffectMap m in SubEffectMaps)
                {
                    m.LoadContent();
                }

                IsContentLoaded = true;
            }
        }

        public void UnloadContent()
        {
            if (IsContentLoaded)
            {
                Engine.Instance.Content.Release(_effectAsset);

                //foreach (KeyValuePair<string, Dictionary<string, ParameterBinding>> t in _parameterCache)
                //{
                //    foreach (KeyValuePair<string, ParameterBinding> p in t.Value)
                //    {
                //        p.Value.UnloadContent(Engine.Content);
                //    }
                //}

                //foreach (KeyValuePair<string, ValueBinding> v in _variableCache)
                //{
                //    v.Value.UnloadContent(Engine.Content);
                //}

                //foreach (KeyValuePair<string, EffectMap> p in SubEffectMaps)
                //{
                //    p.Value.UnloadContent();
                //}

                IsContentLoaded = false;
            }
        }

        #endregion

        #region IDisposableEx Members

        public bool IsDisposed { get; private set; }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
